#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
#include <pthread.h>
#include "global.h"
#include "ietAoLib.h"
#include "prt_log.h"

#define LIB_VERSION(a,b,c)                  ((a << 24) + (b << 16) + (c))

#define AO_MAX_CHANNEL      32              /* AO 最大通道数 */
#if (HARD_VER == 0)
#define AO_AOM_CHANNEL      12              /* AO 在 AO 模块中通道数 */
#elif (HARD_VER == 1)
#define AO_AOM_CHANNEL      12              /* AO 在 AO 模块中通道数 */
#endif

#define AO_DRV_NAME         "pcie_ao"       /* AO 驱动名称 */
#define AO_DEF_PULSE_CYCLE  1000            /* 默认脉冲周期, us */
#define AO_MIN_PULSE_WIDTH  0               /* 最小脉冲占空比, % */
#define AO_MAX_PULSE_WIDTH  50              /* 最大脉冲占空比, % */

#define AO_GP8311_RSET      1               /* GP8311 的 Rset 电阻值, KΩ -> mA */

/* 脉冲参数 */
struct ao_pulse_t {
    uint16_t high;          /* 脉冲高时间, us */
    uint16_t cycle;         /* 脉冲周期, us */
};

/* IOCTL 命令定义 */
#define IOCTL_AO_MAGIC                      'P'                                             /* 标识设备驱动 */
#define IOCTL_AO_SET_PULSE                  _IOW(IOCTL_AO_MAGIC, 1, struct ao_pulse_t)      /* 配置 AO 脉冲 */

#define IOCTL_AO_GET_DRV_VERSION            _IOR(IOCTL_AO_MAGIC, 101, uint32_t)             /* 获取驱动版本号 */
#define IOCTL_AO_GET_FPGA_VERSION           _IOR(IOCTL_AO_MAGIC, 102, uint32_t)             /* 获取 FPGA 版本号 */

struct ThreadCtrl_t {
    char name[64];                          /* 线程名 */
    pthread_t id;                           /* 线程标识 */
};

struct AoDev_t {
    void *parent;                           /* 父指针 */
    char name[32];                          /* 设备名 */
    uint8_t index;                          /* 设备号 */

    struct ao_pulse_t pulse;                /* 脉冲参数 */

    int fd;                                 /* 文件 */
    char path[PATH_MAX];                    /* 路径 */
};

struct AoCtrl_t {
    char name[16];                          /* 控制器名 */
    uint8_t num;                            /* 设备数量 */
    uint8_t max_num;                        /* 最大数量 */
    struct AoDev_t dev[AO_MAX_CHANNEL];     /* 设备描述 */
    uint32_t drvVer;                        /* 驱动 版本号 */
    uint32_t fpgaVer;                       /* FPGA 版本号 */
    uint32_t libVer;                        /* 动态库 版本号 */

    uint8_t type;                           /* 模块类型 */
};
/* AO 控制器 */
static struct AoCtrl_t AoCtrl;

/* 获取设备数量 */
static uint8_t AOMGetDevNum(int type)
{
    uint8_t num = 0;

    switch (type) {
    case IET_ECU:
    case IET_DI:
    case IET_DO:
    case IET_AI:
        goto err_retrun;
    case IET_AO:
        num = AO_AOM_CHANNEL;
        break;
    case IET_PI:
    case IET_SOE:
    case IET_AC:
    case IET_PT100:
    case IET_IEPE:
        goto err_retrun;
    default:
        prt_erro("Invalid type %d for %s\n", type, __func__);
        return 0;
    }
    prt_info("%s get %d device\n", __func__, num);

    return num;

err_retrun:
    prt_erro("%s get no device\n", __func__);

    return 0;
};

/* 打开设备 */
int AOMOpen(int moduleType)
{
    struct AoCtrl_t *pctrl = &AoCtrl;
    struct AoDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;
    int8_t j = 0;

    pctrl->num = AOMGetDevNum(moduleType);
    pctrl->max_num = AO_MAX_CHANNEL;
    pctrl->type = (uint8_t)moduleType;
    /* 检查参数 */
    if (pctrl->num > pctrl->max_num) {
        prt_erro("Get AO number %d, max is %d\n", pctrl->num, pctrl->max_num);
        return -EFAULT;
    }
    /* 生成路径 */
    snprintf(pctrl->name, sizeof(pctrl->name), "%s", AO_DRV_NAME);
    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        pdev->parent = pctrl;
        snprintf(pdev->name, sizeof(pdev->name), "%s%d", pctrl->name, i);
        pdev->index = i;
        pdev->fd = -ENODEV;
        ret = snprintf(pdev->path, PATH_MAX, "/dev/%s", pdev->name);
        if (ret < 0 || ret >= PATH_MAX) {
            prt_erro("Generate %s path failed\n", pdev->name);
            return -ENAMETOOLONG;
        }
        /* 打开设备 */
        pdev->fd = open(pdev->path, O_RDWR);
        if (pdev->fd < 0) {
            ret = errno;
            prt_erro("Open %s device failed, %s\n", pdev->path, strerror(ret));
            ret = -ret;
            goto err_close_fd;
        }
        prt_dbug("Open %s device success\n", pdev->path);
    }

    pdev = &pctrl->dev[0];
    /* 获取驱动版本号 */
    ret = ioctl(pdev->fd, IOCTL_AO_GET_DRV_VERSION, &pctrl->drvVer);
    if (ret < 0) {
        prt_erro("Get AO driver version 0x%08x failed\n", pctrl->drvVer);
        goto err_close_fd;
    }
    prt_dbug("Get AO driver version 0x%08x\n", pctrl->drvVer);
    /* 获取 FPGA 版本号 */
    ret = ioctl(pdev->fd, IOCTL_AO_GET_FPGA_VERSION, &pctrl->fpgaVer);
    if (ret < 0) {
        prt_erro("Get AO FPGA version 0x%08x failed\n", pctrl->fpgaVer);
        goto err_close_fd;
    }
    prt_dbug("Get AO FPGA version 0x%08x\n", pctrl->fpgaVer);
    pctrl->libVer = LIB_VERSION(AO_VER_MAJOR, AO_VER_MINOR, AO_VER_PATCH);
    prt_dbug("Get AO library version 0x%08x\n", pctrl->libVer);

    return 0;

err_close_fd:
    for (j = i - 1; j >= 0; j--) {
        pdev = &pctrl->dev[j];
        close(pdev->fd);
        pdev->fd = -ENODEV;
    }

    return ret;
}

/* 初始化设备 */
int AOMInit()
{
    struct AoCtrl_t *pctrl = &AoCtrl;
    struct AoDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;

    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
    }

    return ret;
}

/* 写设备真实值 */
int AOMWriteReal(int channel, double data)
{
    struct AoCtrl_t *pctrl = &AoCtrl;
    struct AoDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;
    uint8_t start = 0;
    uint8_t end = 0;

    /* 检查参数 */
    if (channel < 0) {
        prt_erro("Invalid parameters for %s\n", __func__);
        return -EFAULT;
    }
    if (channel >= pctrl->num) {
        start = 0;
        end = pctrl->num;
    } else {
        start = channel;
        end = channel + 1;
    }
    for (i = start; i < end; i++) {
        pdev = &pctrl->dev[i];
        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        pdev->pulse.cycle = AO_DEF_PULSE_CYCLE;
        /* 根据电流计算脉冲参数 */
        /* GP8311 Iout=Duty*5V*10/Rset (Rset=1K) */
        pdev->pulse.high = 1.0 * (pdev->pulse.cycle * (data * AO_GP8311_RSET) / (5 * 10));
        /* 配置 AO 脉冲 */
        ret = ioctl(pdev->fd, IOCTL_AO_SET_PULSE, &pdev->pulse);
        if (ret < 0) {
            prt_erro("Set AO %2d current %.6lfmA failed\n", pdev->index, data);
            return ret;
        }
        prt_info("Set AO %2d current %.6lfmA success\n", pdev->index, data);
    }

    return ret;
}

int AOMClose()
{
    struct AoCtrl_t *pctrl = &AoCtrl;
    struct AoDev_t *pdev = &pctrl->dev[0];
    int ret = 0;
    uint8_t i = 0;

    for (i = 0; i < pctrl->num; i++) {
        pdev = &pctrl->dev[i];

        /* 检查参数 */
        if (pdev->fd == 0) {
            prt_erro("Invalid %s->fd for %s\n", pdev->name, __func__);
            return -EFAULT;
        }
        // pdev->curr.val = 0;
        // pdev->curr.cycle = AO_DEF_PULSE_CYCLE;
        // /* 配置 AO 电流 */
        // ret = ioctl(pdev->fd, IOCTL_AO_SET_CURRENT, &pdev->curr);
        // if (ret < 0) {
        //     prt_erro("Set AO %2d current %dmA failed\n", pdev->index, pdev->curr.val);
        //     return -EFAULT;
        // }
        // prt_dbug("Set AO %2d current %dmA success\n", pdev->index, pdev->curr.val);
        /* 关闭设备 */
        ret = close(pdev->fd);
        if (ret != 0) {
            prt_erro("Close %s failed\n", pdev->path);
            return -EFAULT;
        }
        pdev->fd = 0;
        prt_dbug("Close %s success\n", pdev->path);
    }

    return ret;
}

/* 获取驱动版本号 */
int AOMGetDrvVer()
{
    struct AoCtrl_t *pctrl = &AoCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->drvVer >> 24) & 0xFF), 
             ((pctrl->drvVer >> 16) & 0xFF), (pctrl->drvVer & 0xFFFF));

    return (int)(pctrl->drvVer >> 16);
}

/* 获取 FPGA 版本号 */
int AOMGetFpgaVer()
{
    struct AoCtrl_t *pctrl = &AoCtrl;

    prt_dbug("%s %d.%d\n", __func__, ((pctrl->fpgaVer >> 8) & 0xFF), 
             (pctrl->fpgaVer & 0xFF));

    return (int)(pctrl->fpgaVer);
}

/* 获取动态库版本号 */
int AOMGetLibVer()
{
    struct AoCtrl_t *pctrl = &AoCtrl;

    prt_dbug("%s %d.%d.%d\n", __func__, ((pctrl->libVer >> 24) & 0xFF), 
             ((pctrl->libVer >> 16) & 0xFF), (pctrl->libVer & 0xFFFF));

    return (int)(pctrl->libVer >> 16);
}
